import requests
import hashlib
import json
import os
import logging
from typing import Dict, Any, Optional
from datetime import datetime, timedelta

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger('bus_api')


class BusApiClient:
    """公交API客户端"""
    
    BASE_URL = "http://api.wxbus163.cn/z_busapi/BusApi.php"
    CACHE_DIR = "cache"
    CITIES_CACHE_FILE = os.path.join(CACHE_DIR, "cities_cache.json")
    CACHE_EXPIRY_DAYS = 7  # 城市数据缓存过期时间（天）
    
    def __init__(self, username: str, api_key: str):
        """
        初始化API客户端
        
        Args:
            username: API账号用户名
            api_key: API密钥
        """
        self.username = username
        self.api_key = api_key
        
        # 确保缓存目录存在
        if not os.path.exists(self.CACHE_DIR):
            os.makedirs(self.CACHE_DIR)
            logger.info(f"创建缓存目录: {self.CACHE_DIR}")
    
    def _generate_signature(self, op_type: str) -> str:
        """
        生成API签名
        
        Args:
            op_type: 操作类型
            
        Returns:
            签名字符串
        """
        # 签名规则：MD5(uname+key+optype)
        sign_str = self.username + self.api_key + op_type
        signature = hashlib.md5(sign_str.encode()).hexdigest()
        logger.debug(f"生成签名: op_type={op_type}, signature={signature}")
        return signature
    
    def _make_request(self, params: Dict[str, Any]) -> Dict[str, Any]:
        """
        发送API请求
        
        Args:
            params: 请求参数
            
        Returns:
            API响应结果
        """
        # 记录请求信息（去除敏感信息）
        log_params = params.copy()
        if 'keySecret' in log_params:
            log_params['keySecret'] = '******'  # 隐藏密钥
            
        logger.info(f"发送API请求: {self.BASE_URL} 参数: {log_params}")
        
        try:
            response = requests.get(self.BASE_URL, params=params)
            response.raise_for_status()
            result = response.json()
            
            # 记录响应结果
            if result.get('return_code') == 'ok':
                # 为避免日志过大，当成功时只记录部分信息
                log_result = {
                    'return_code': result.get('return_code'),
                    'error_code': result.get('error_code')
                }
                
                # 添加结果数量信息
                if 'returl_list' in result:
                    returl_list = result['returl_list']
                    if isinstance(returl_list, list):
                        log_result['returl_list_count'] = len(returl_list)
                        if len(returl_list) > 0:
                            log_result['returl_list_sample'] = returl_list[0]
                
                logger.info(f"API响应成功: {log_result}")
            else:
                # 出错时记录完整响应
                logger.error(f"API响应错误: {result}")
                
            return result
        except Exception as e:
            logger.error(f"API请求异常: {str(e)}")
            raise
    
    def _is_cache_valid(self, cache_file: str) -> bool:
        """
        检查缓存是否有效
        
        Args:
            cache_file: 缓存文件路径
            
        Returns:
            缓存是否有效
        """
        if not os.path.exists(cache_file):
            logger.debug(f"缓存文件不存在: {cache_file}")
            return False
            
        file_modified_time = datetime.fromtimestamp(os.path.getmtime(cache_file))
        cache_expiry = datetime.now() - timedelta(days=self.CACHE_EXPIRY_DAYS)
        
        is_valid = file_modified_time > cache_expiry
        logger.debug(f"检查缓存有效性: {cache_file}, 修改时间: {file_modified_time}, 是否有效: {is_valid}")
        return is_valid
    
    def _get_bus_lines_cache_path(self, city_id: str, bus_line: str) -> str:
        """
        获取公交路线缓存文件路径
        
        Args:
            city_id: 城市ID
            bus_line: 公交线路名称
            
        Returns:
            缓存文件路径
        """
        # 文件名中不能包含特殊字符，使用简单的替换方式处理
        safe_bus_line = bus_line.replace('/', '_').replace('\\', '_')
        return os.path.join(self.CACHE_DIR, f"bus_line_{city_id}_{safe_bus_line}.json")
    
    def _cache_bus_lines(self, city_id: str, bus_line: str, forward_data: Dict, reverse_data: Dict = None) -> None:
        """
        缓存公交路线信息（包括正向和反向路线）
        
        Args:
            city_id: 城市ID
            bus_line: 公交线路名称
            forward_data: 正向路线数据
            reverse_data: 反向路线数据，可为None
        """
        cache_file = self._get_bus_lines_cache_path(city_id, bus_line)
        
        # 构建缓存数据结构
        cache_data = {
            "timestamp": datetime.now().isoformat(),
            "city_id": city_id,
            "bus_line": bus_line,
            "forward_data": forward_data,
            "reverse_data": reverse_data
        }
        
        try:
            logger.info(f"保存公交路线缓存: {cache_file}")
            with open(cache_file, 'w', encoding='utf-8') as f:
                json.dump(cache_data, f, ensure_ascii=False, indent=2)
        except Exception as e:
            logger.error(f"缓存公交路线数据时出错: {e}")
    
    def _get_cached_bus_lines(self, city_id: str, bus_line: str) -> Optional[Dict[str, Any]]:
        """
        获取缓存的公交路线信息
        
        Args:
            city_id: 城市ID
            bus_line: 公交线路名称
            
        Returns:
            缓存的公交路线数据，如果没有有效缓存返回None
        """
        cache_file = self._get_bus_lines_cache_path(city_id, bus_line)
        
        if self._is_cache_valid(cache_file):
            try:
                logger.info(f"使用公交路线缓存: {cache_file}")
                with open(cache_file, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except (json.JSONDecodeError, FileNotFoundError) as e:
                logger.warning(f"读取公交路线缓存失败: {str(e)}")
        
        return None
    
    def get_cities(self) -> Dict[str, Any]:
        """
        获取城市列表，优先使用缓存
        
        Returns:
            城市列表数据
        """
        # 检查是否有有效的缓存数据
        if self._is_cache_valid(self.CITIES_CACHE_FILE):
            try:
                logger.info(f"使用城市列表缓存: {self.CITIES_CACHE_FILE}")
                with open(self.CITIES_CACHE_FILE, 'r', encoding='utf-8') as f:
                    cached_data = json.load(f)
                    return cached_data
            except (json.JSONDecodeError, FileNotFoundError) as e:
                logger.warning(f"读取城市列表缓存失败: {str(e)}")
                # 缓存文件损坏或不存在，重新请求数据
                pass
                
        # 没有有效缓存，请求新数据
        logger.info("无有效缓存，从API请求城市列表")
        params = {
            "optype": "city",
            "uname": self.username
        }
        
        result = self._make_request(params)
        
        # 缓存结果
        if result.get("return_code") == "ok":
            try:
                logger.info(f"保存城市列表缓存: {self.CITIES_CACHE_FILE}")
                with open(self.CITIES_CACHE_FILE, 'w', encoding='utf-8') as f:
                    json.dump(result, f, ensure_ascii=False, indent=2)
            except Exception as e:
                logger.error(f"缓存城市数据时出错: {e}")
                
        return result
    
    def search_bus_lines(self, city_id: str, keywords: str) -> Dict[str, Any]:
        """
        搜索公交路线，优先使用缓存
        
        Args:
            city_id: 城市ID
            keywords: 搜索关键词，如"3"代表3路公交
            
        Returns:
            公交路线数据
        """
        logger.info(f"搜索公交路线: city_id={city_id}, keywords={keywords}")
        
        # 首先尝试从缓存获取
        cached_data = self._get_cached_bus_lines(city_id, keywords)
        if cached_data:
            logger.info(f"使用缓存的公交路线数据: {keywords}")
            forward_data = cached_data.get("forward_data", {})
            return forward_data
        
        # 没有缓存，请求新数据
        op_type = "luxian"
        key_secret = self._generate_signature(op_type)
        
        params = {
            "optype": op_type,
            "uname": self.username,
            "cityid": city_id,
            "keywords": keywords,
            "keySecret": key_secret
        }
        
        forward_result = self._make_request(params)
        
        # 先不保存缓存，等获取反向路线后一起保存
        return forward_result
    
    def search_bus_lines_with_reverse(self, city_id: str, keywords: str) -> Dict[str, Any]:
        """
        搜索公交路线，包括正向和反向路线，优先使用缓存
        
        Args:
            city_id: 城市ID
            keywords: 搜索关键词
            
        Returns:
            包含正向和反向路线的数据
        """
        logger.info(f"搜索公交路线(含反向): city_id={city_id}, keywords={keywords}")
        
        # 首先尝试从缓存获取
        cached_data = self._get_cached_bus_lines(city_id, keywords)
        if cached_data:
            logger.info(f"使用缓存的公交路线数据(含反向): {keywords}")
            return {
                "forward_data": cached_data.get("forward_data", {}),
                "reverse_data": cached_data.get("reverse_data", {})
            }
        
        # 获取正向路线
        forward_result = self.search_bus_lines(city_id, keywords)
        
        # 获取反向路线
        reverse_result = self.search_bus_lines_reverse(city_id, keywords)
        
        # 缓存正向和反向路线数据
        if forward_result.get("return_code") == "ok":
            self._cache_bus_lines(city_id, keywords, forward_result, reverse_result)
        
        return {
            "forward_data": forward_result,
            "reverse_data": reverse_result
        }
    
    def search_bus_lines_reverse(self, city_id: str, keywords: str) -> Dict[str, Any]:
        """
        搜索公交反向路线
        
        Args:
            city_id: 城市ID
            keywords: 搜索关键词，如"3"代表3路公交
            
        Returns:
            公交反向路线数据
        """
        logger.info(f"搜索公交反向路线: city_id={city_id}, keywords={keywords}")
        op_type = "luxian"
        key_secret = self._generate_signature(op_type)
        
        # 添加反向查询参数
        params = {
            "optype": op_type,
            "uname": self.username,
            "cityid": city_id,
            "keywords": keywords,
            "keySecret": key_secret,
            "isdirect": 1  # 1代表反向查询
        }
        
        return self._make_request(params)
    
    def get_realtime_bus_location(self, city_id: str, bus_linestrid: str, 
                                  bus_linenum: str, bus_staname: str) -> Dict[str, Any]:
        """
        获取公交实时位置
        
        Args:
            city_id: 城市ID
            bus_linestrid: 路线ID
            bus_linenum: 路线编号
            bus_staname: 路线名称
            
        Returns:
            公交实时位置数据
        """
        logger.info(f"获取公交实时位置: city_id={city_id}, bus_linestrid={bus_linestrid}, bus_linenum={bus_linenum}, bus_staname={bus_staname}")
        op_type = "rtbus"
        key_secret = self._generate_signature(op_type)
        
        params = {
            "optype": op_type,
            "uname": self.username,
            "cityid": city_id,
            "bus_linestrid": bus_linestrid,
            "bus_linenum": bus_linenum,
            "bus_staname": bus_staname,
            "keySecret": key_secret
        }
        
        return self._make_request(params)
    
    def get_bus_schedule(self, city_id: str, bus_staname: str) -> Dict[str, Any]:
        """
        获取发车时刻表
        
        Args:
            city_id: 城市ID
            bus_staname: 路线名称
            
        Returns:
            发车时刻表数据
        """
        logger.info(f"获取发车时刻表: city_id={city_id}, bus_staname={bus_staname}")
        op_type = "btimept"
        key_secret = self._generate_signature(op_type)
        
        params = {
            "optype": op_type,
            "uname": self.username,
            "cityid": city_id,
            "bus_staname": bus_staname,
            "keySecret": key_secret
        }
        
        return self._make_request(params)
    
    def search_location(self, city_name: str, keywords: str) -> Dict[str, Any]:
        """
        换乘-地点搜索
        
        Args:
            city_name: 城市名称
            keywords: 搜索关键词
            
        Returns:
            地点搜索结果
        """
        logger.info(f"搜索地点: city_name={city_name}, keywords={keywords}")
        op_type = "tfrone"
        key_secret = self._generate_signature(op_type)
        
        params = {
            "optype": op_type,
            "uname": self.username,
            "cityname": city_name,
            "keywords": keywords,
            "keySecret": key_secret
        }
        
        return self._make_request(params)
    
    def plan_route(self, start_station: str, start_imlgn: str, 
                   end_station: str, end_imlgn: str) -> Dict[str, Any]:
        """
        换乘-路线规划
        
        Args:
            start_station: 起始地址
            start_imlgn: 起始地址标识
            end_station: 目的地地址
            end_imlgn: 目的地标识
            
        Returns:
            路线规划结果
        """
        logger.info(f"规划换乘路线: start_station={start_station}, end_station={end_station}")
        op_type = "tfrtwo"
        key_secret = self._generate_signature(op_type)
        
        params = {
            "optype": op_type,
            "uname": self.username,
            "stasta": start_station,
            "staimlgn": start_imlgn,
            "endsta": end_station,
            "endimlgn": end_imlgn,
            "keySecret": key_secret
        }
        
        return self._make_request(params) 